#pragma once

#include "types.h"

// Piles représentant l'état de la machine cible au point du programme en cours
// de compilation.

/********************************* Types **************************************/

Type type_entier(int is_const);
Type type_pointeur(Type pointee, int is_const);
Type type_fonction(/*TBD*/);

void type_setConst(Type* t, int is_const);

// si Vrai, désigne une valeur connue lors de la compilation. La valeur que ce
// type accompagne sera alors une valeur, et non une adresse. Implique Const.
int type_knownValue(Type t);
void type_setKnownValue(Type* t, int k);

char* type_repr(Type t); // must be free() by caller.

// 0 si différents
int type_equal(Type t1, Type t2);

// 0 si OK pour affecter t2 dans t1, -1 si t1 protégé par const, 
// -2 si les types ne correspondent pas, 
// -3 si les 'const's interdisent l'affectation dans ce sens.
int type_canAffect(Type t1, Type t2);

// 1 pour Oui.
int type_canCompare(Type t1, Type t2);


/******************************* Fonctions ************************************/

ListeArguments argListe_new();
void argListe_add(ListeArguments* l, Type type); // ajoute à la fin
int argListe_equal(ListeArguments l1, ListeArguments l2);

// renvoie un identifiant unique pour la fonction. Si rappelé avec le même nom, 
// renvoie le même ID qu'au premier appel si les signatures correspondent, 
// exit() sur erreur sinon.
int tabFonction_add(const char* name, Type typeRetour, ListeArguments listeArguments, int lineDecl);

// -1 si pas encore déclarée à ce stade.
int tabFonction_get(const char* name);

// exit sur erreur si la fonction a déjà un corps.
void tabFonction_beginCorps(int fonction, int addr, int lineDef);
void tabFonction_endCorps(int function, int addr);

// renvoie -1 si fonction pas connue, ou n'a pas de corps. ID de la fonction sinon.
int tabFonction_getCorps(const char* name, int* pDebut, int* pFin);

// A appeler si on force le déplacement du corps après sa compilation.
// Comportement ultérieur de findLastGap() indéfini si l'ordre des fonctions
// dans le fichier de sortie est modifié.
void tabFonction_corpsMoved(int fnct, int deplacement);

// Détermine le prochain déplacement de code à effectuer pour coller toutes les 
// fonctions ensembles à la fin du fichier. Renvoie l'ID de la fonction à 
// déplacer (ou -1 si aucun déplacement n'est nécessaire) (à passer à corps_moved),
// et fait sortir les paramètres à passer à output_inverse.
int tabFonction_findLastGap(int* pDebutFun, int* pFinFun, int* pFinGap);

/*************************** Table des symboles *******************************/

// Ajoute un nom au sommet du contexte.
// 'init': vrai si la valeur est définie, pour permettre la détection des 
//         utilisations de variables non définies
// 'lineDecl': # de ligne de la déclaration dans le fichier source, pour les 
//             messages d'avertissement.
// Renvoie l'addresse de la nouvelle variable.
// exit() sur erreur si impossible d'ajouter car le nom est déjà pris.
Addr tabSymboles_push(Type t, const char* name, int isGlobal, int lineDecl);
// Comportement indéfini si taille du tableau invalide.
Addr tabSymboles_pushArray(Type t, int size, const char* name, int isGlobal, int lineDecl);

// Espace occupé par toutes les variables globales connues à ce stade.
int tabSymboles_nbOctetsVariablesGlobales();

// Renvoie l'adresse de la variable donnée. -1 si non trouvée.
// Si définie plusieurs fois, renvoie la variable avec la portée la plus 
//    intérieure (de la Frame la plus haute).
// 'pType': Peut être NULL. Sortie optionnelle du type de la variable.
Addr tabSymboles_find(const char* name, Type* pType);

// Comportement indéfini si il n'y a pas effectivement de variable à l'adresse.
const char* tabSymboles_nameByAddr(Addr addr);
int tabSymboles_declLineByAddr(Addr addr);

// A appeler à l'affectation d'une variable.
void tabSymboles_flagInit(Addr var_addr);
int tabSymboles_isInit(Addr var_addr);

// Empile une variable anonyme, et renvoie son addresse
Addr tabSymboles_pushTmp();
// dépile la dernière variable créée.
void tabSymboles_popTmp();
int tabSymboles_isTmp(Addr addr);
// tant qu'une adresse est protégée, isTmp() va mentir en répondant 'non'.
void tabSymboles_protectTmp(Addr addr, int protect);


// Finalise l'étage de pile courant: toutes les variables du nouvel étage
// seront libérées lors du prochain 'popFrame()'
void tabSymboles_pushFrame();

// équilibre le dernier 'pushFrame()'
void tabSymboles_popFrame();


/***************************** Contrôle de flux *******************************/

// Besoin d'une autre pile, j'ai pas réussi à tout caser dans la pile de yacc

typedef enum {
   CTRL_IF,
   CTRL_BOUCLE,
} CtrlType;

// nombre maximal de 'break' dans une boucle ou un switch-case
#define MAX_BREAKS 64

// étage de contexte pour un motif (if, while, ...)
typedef struct 
{
   CtrlType type;
   
   // Chaque motif peut n'utiliser que certains champs.
   int addr0; // début eval du premier test (+convergence pour la boucle for)
	int addr1; // premier saut (JMF)
	int addr2; // deuxième saut (JMP)
   
	Addr arg; // adresse à utiliser dans un branchement
   
   int breaks[MAX_BREAKS];
   int cBreaks;
} ControlFrame;

extern ControlFrame* flowCtrl_top;

void flowCtrl_push(CtrlType ty);

// May return NULL
ControlFrame* flowCtrl_innerLoop();

// Les 'break' seront remplacés par des JMPs dès que l'adresse de saut sera
// connue: lors du flowCtrl_pop()
void flowCtrl_addBreak(int addr);

// addr: point où les 'break' à l'intérieur du corps doivent converger.
void flowCtrl_pop(int addr);
